﻿using System;
using System.Collections.Generic;
using System.Web;
using System.Threading;

namespace StockTrader.QuoteCenter.Streamer.BLL
{
    public class AsyncCore : IAsyncResult
    {
        private readonly AsyncCallback _asyncCallback;

        private readonly Object _asyncState;

        private const int _statePending = 0;
        private const int _stateCompletedSynchronously = 1;
        private const int _stateCompletedAsynchronously = 2;

        private int _completeState = _statePending;
        private ManualResetEvent _asyncWaitHandle;
        private Exception _exception;

        public AsyncCore(AsyncCallback asynCallback, Object state)
        {
            _asyncCallback = asynCallback;
            _asyncState = state;
        }

        public void SetAsCompeleted(Exception exception, Boolean completedSynchronously)
        {
            _exception = exception;
            Int32 prevState = Interlocked.Exchange(ref _completeState, completedSynchronously ? _stateCompletedSynchronously : _stateCompletedAsynchronously);
            if (prevState == _statePending)
                throw new InvalidOperationException("You can set a result only once");
            if (_asyncWaitHandle != null) _asyncWaitHandle.Set();
            if (_asyncCallback != null) _asyncCallback(this);
        }

        public void EndInvoke()
        {
            if (!IsCompleted)
            {
                AsyncWaitHandle.WaitOne();
                AsyncWaitHandle.Close();
                _asyncWaitHandle = null;
            }

            if (_exception != null) throw _exception;
        }

        #region IAsyncResult Members

        public object AsyncState { get { return _asyncState; } }

        public WaitHandle AsyncWaitHandle
        {
            get
            {
                if (_asyncWaitHandle == null)
                {
                    Boolean done = IsCompleted;
                    ManualResetEvent mre = new ManualResetEvent(done);
                    if (Interlocked.CompareExchange(ref _asyncWaitHandle, mre, null) != null)

                        mre.Close();
                    else
                    {
                        if (!done && IsCompleted)
                            _asyncWaitHandle.Set();
                    }
                }
                return _asyncWaitHandle;
            }
        }

        public bool CompletedSynchronously
        {
            get { return Thread.VolatileRead(ref _completeState) == _stateCompletedSynchronously; }
        }

        public bool IsCompleted
        {
            get { return Thread.VolatileRead(ref _completeState) == _statePending; }
        }

        #endregion

        public class AsyncResult<TResult> : AsyncCore
        {
            private TResult _result = default(TResult);
            public AsyncResult(AsyncCallback asyncCallback, Object state) :
                base(asyncCallback, state)
            {

            }

            public void SetAsCompeleted(TResult result, Boolean completedSynchronously)
            {
                _result = result;
                base.SetAsCompeleted(null, completedSynchronously);
            }

            new public TResult EndInvoke()
            {
                base.EndInvoke();
                return _result;
            }
        }
    }
}
